
// Copyright (C) 2009-2011 Luca Piccioni
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//   
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;

namespace Derm.Raster
{
	/// <summary>
	/// Generic image.
	/// </summary>
	/// <remarks>
	/// <para>
	/// The Image class abstract an image: an image is a set of rectangle areas of pixels. Each
	/// rectangle area is defined as <i>layer</i>. Each layer could have different meanings, depending
	/// on the implementation of the Image class.
	/// </para>
	/// <para>
	/// Tipical RGB images has only one layer, and each pixel define the red, green and blue components
	/// defining the pixel color. Neverless, it is possible to define additional layers to define
	/// image mipmaps, which are similar to the original layer but with lower resolutions.
	/// </para>
	/// <para>
	/// Another application of the Image layers is the definition of Luma-Chroma images. The final pixel
	/// color is defined by the components of the three layers in the image. The resulting image pixel color
	/// is obtained by sampling the pixel for each layer and transform it using color space convertion
	/// matrices.
	/// </para>
	/// </remarks>
	public abstract class Image
	{
		#region Constructors

		/// <summary>
		/// Default constructor.
		/// </summary>
		public Image() : this(0)
		{
		
		}

		/// <summary>
		/// Constructor that specifies Image modifiers.
		/// </summary>
		/// <param name="modifiers">
		/// 
		/// </param>
		public Image(ModifierFlags modifiers)
		{
			// Modifiers are immutable
			mModifiers = modifiers;
		}

		#endregion

		#region Image Pixel Format

		/// <summary>
		/// Image pixel format.
		/// </summary>
		/// <returns>
		/// It returns a <see cref="Pixel.Type"/> that specifies the image color resolution.
		/// </returns>
		/// <remarks>
		/// This property shall be only an indicative value about the image color resolution, dst
		/// allow the best memory management due color precision. Actually this property shall
		/// not be used to determine image layering (i.e. YUV formats imply three layers).
		/// </remarks>
		public abstract Pixel.Type PixelFormat { get; }

		#endregion

		#region Image Layering

		/// <summary>
		/// Image layers count.
		/// </summary>
		public int LayerCount
		{
			get { return (mLayerCount); }
			protected set {
				Debug.Assert(value > 0, "Image.LayerCount.set", "Invalid value " + value);
				mLayerCount = value;
			}
		}

		/// <summary>
		/// Image layers count.
		/// </summary>
		private int mLayerCount = 0;

		#endregion

		#region Image Pixel Data

		/// <summary>
		/// Allocate pixel data for this Image.
		/// </summary>
		/// <param name="w">
		/// A <see cref="System.Int32"/> indicating the image width, in pixels.
		/// </param>
		/// <param name="h">
		/// A <see cref="System.Int32"/> indicating the image height, in pixels.
		/// </param>
		/// <remarks>
		/// This routine could be called to allocate image layers. Once allocated, the image
		/// pixels can be accessed using the Image indexer. Actually the behavior of this
		/// routine depends on the current Image implementation, but the following behavior
		/// shall be expected:
		/// - Store the image extents, which shall corresponds with the <paramref name="w"/> and
		///   <paramref name="h"/> parameters.
		/// </remarks>
		public abstract void AllocatePixelData(uint w, uint h);

		/// <summary>
		/// Get pixel data buffer representing the first image layer.
		/// </summary>
		/// <returns>
		/// This property shall returns an object representing the image pixel data. Tipically it will
		/// be a array of packed structures representing a pixel element.
		/// </returns>
		public object GetPixelData() { return (GetPixelData(0)); }

		/// <summary>
		/// Pixel data buffer of any defined image layer.
		/// </summary>
		/// <remarks>
		/// This property shall returns an object representing the image pixel data. Tipically it will
		/// be a array of packed structures representing a pixel element.
		/// </remarks>
		public abstract object GetPixelData(uint layer);

		/// <summary>
		/// Pixel data accessor for first image layer.
		/// </summary>
		/// <param name="x">
		/// A <see cref="System.Int32"/> indicating the horizontal coordinate of the pixel. The
		/// left corners has the coordinate 0.
		/// </param>
		/// <param name="y">
		/// A <see cref="System.Int32"/> indicating the vertical coordinate of the pixel. The
		/// bottom corners has the coordinate 0.
		/// </param>
		/// <returns></returns>
		public IColor this[uint x, uint y] { get { return (this[0, x, y]); } set { this[0, x, y] = value; } }

		/// <summary>
		/// Pixel data accessor.
		/// </summary>
		/// <param name="layer">
		/// A <see cref="System.Int32"/> indicating the layer to fetch pixel data.
		/// </param>
		/// <param name="x">
		/// A <see cref="System.Int32"/> indicating the horizontal coordinate of the pixel. The
		/// left corners has the coordinate 0.
		/// </param>
		/// <param name="y">
		/// A <see cref="System.Int32"/> indicating the vertical coordinate of the pixel. The
		/// bottom corners has the coordinate 0.
		/// </param>
		/// <returns>
		/// 
		/// </returns>
		/// <remarks>
		/// <para>
		/// This indexer allow an uniform access to the Image pixel data. Since the data could be stored
		/// in many data types (see <see cref="Pixel.Type"/> enumeration), the only color type able to
		/// map every possible value is the <see cref="ColorRGBAF"/>.
		/// </para>
		/// </remarks>
		public abstract IColor this[uint layer, uint x, uint y] { get; set; }

		/// <summary>
		/// Image width property.
		/// </summary>
		public uint Width
		{
			get { return (mWidth); }
			protected set {
				Debug.Assert(value >= 0);
				mWidth = value;
			}
		}
		
		/// <summary>
		/// Image height property.
		/// </summary>
		public uint Height
		{
			get { return (mHeight); }
			protected set {
				mHeight = value;
			}
		}

		/// <summary>
		/// Image pixel size, in bytes
		/// </summary>
		public uint PixelSize
		{
			get {
				return (mPixelSize);
			}
		}

		/// <summary>
		/// Image line width, in bytes
		/// </summary>
		public uint Stride
		{
			get {
				return (mWidth * mPixelSize);
			}
		}

		/// <summary>
		/// Image size, in bytes.
		/// </summary>
		public uint Size
		{
			get {
				return (mWidth * mHeight * mPixelSize);
			}
		}
		
		/// <summary>
		/// RasterImage width.
		/// </summary>
		private uint mWidth = 0;

		/// <summary>
		/// RasterImage height.
		/// </summary>
		private uint mHeight = 0;

		/// <summary>
		/// RasterImage line width, in bytes.
		/// </summary>
		protected uint mPixelSize = 0;

		#endregion

		#region Image Modifiers

		/// <summary>
		/// Image modifier flags.
		/// </summary>
		[Flags()]
		public enum ModifierFlags : ushort
		{
			/// <summary>
			/// No flag.
			/// </summary>
			None =						0x0000,
		}

		/// <summary>
		/// This Image modifiers.
		/// </summary>
		public bool HasModifiers(ModifierFlags flags)
		{
			return ((mModifiers & flags) == flags);
		}

		/// <summary>
		/// Image modifiers.
		/// </summary>
		private ModifierFlags mModifiers = ModifierFlags.None;

		#endregion

		#region Conversions

		/// <summary>
		/// Convert this image to another pixel type.
		/// </summary>
		/// <param name="dstFormat">
		/// A <see cref="Pixel.Type"/> that specifies the destination image pixel type.
		/// </param>
		/// <returns>
		/// It returns an <see cref="Image"/> visually equalivalent to this Image, but having a pixel format corresponding to
		/// <paramref name="dstFormat"/>
		/// </returns>
		/// <exception cref="ArgumentException">
		/// Exception thrown in the case <paramref name="dstFormat"/> is <see cref="Pixel.Type.None"/>.
		/// </exception>
		/// <exception cref="InvalidOperationException">
		/// Exception thrown in the case the conversion from <see cref="PixelFormat"/> to <paramref name="dstFormat"/> is not
		/// implemented. To check for image conversion support, check <see cref="Pixel.IsConvertionSupported"/>.
		/// </exception>
		public Image Convert(Pixel.Type dstFormat)
		{
			Image srcImage = this, dstImage;

			if (dstFormat == Pixel.Type.None)
				throw new ArgumentException("invalid conversion pixel format " + dstFormat, "dstFormat");
			if (Pixel.IsConvertionSupported(PixelFormat, dstFormat) == false)
				throw new InvalidOperationException(String.Format("pixel convetion from {0} to {1} not implemented", PixelFormat, dstFormat));

			// Allocate destination image
			dstImage = Image.CreateImage(Width, Height, dstFormat);
			// Convert this image
			Pixel.Convert(srcImage.GetPixelData(), PixelFormat, dstImage.GetPixelData(), dstFormat, Width, Height);

			return (dstImage);
		}

		#endregion

		#region Image Transformation

		#region Colors

		/// <summary>
		/// Transform this image to the negative color image.
		/// </summary>
		public void Negative()
		{
			GCHandle hImageData = GCHandle.Alloc(GetPixelData(), GCHandleType.Pinned);

			try {
				unsafe {
					byte* hImageDataPtr = (byte*)hImageData.AddrOfPinnedObject().ToPointer();
					byte* hImageDataPtrEnd = hImageDataPtr + Stride * Height;

					for (; hImageDataPtr < hImageDataPtrEnd;) {
						switch (PixelFormat) {
							case Pixel.Type.GRAY8:
							case Pixel.Type.RGB24:
							case Pixel.Type.BGR24:
								NegateColorComponent((byte*)hImageDataPtr);
								hImageDataPtr += 1;
								break;
							case Pixel.Type.GRAY16:
							case Pixel.Type.RGB48:
							case Pixel.Type.BGR48:
								NegateColorComponent((ushort*)hImageDataPtr);
								hImageDataPtr += 2;
								break;
							default:
								throw new InvalidOperationException("pixel format " + PixelFormat + " is unsupported for negate colors");
						}
					}
				}
			} finally {
				hImageData.Free();
			}
		}

		private unsafe void NegateColorComponent(byte *color) { *color = (byte)(Byte.MaxValue - *color); }

		private unsafe void NegateColorComponent(ushort *color) { *color = (ushort)(UInt16.MaxValue - *color); }

		#endregion

		#region Flipping

		/// <summary>
		/// Flip the image vertically.
		/// </summary>
		public void FlipVertically()
		{
			GCHandle hImageData = GCHandle.Alloc(GetPixelData(), GCHandleType.Pinned);
			byte[] tmpScanline = new byte[Stride];

			try {
				unsafe {
					byte* hImageDataPtrBottom = (byte*)hImageData.AddrOfPinnedObject().ToPointer();
					byte* hImageDataPtrTop = hImageDataPtrBottom + Stride * (Height - 1);

					while (hImageDataPtrBottom < hImageDataPtrTop) {
						// Copy to temporary scaline
						Marshal.Copy(new IntPtr((void*)hImageDataPtrTop), tmpScanline, 0, (int)Stride);
						// Swap scan lines
						memcpy((void*)hImageDataPtrTop, (void*)hImageDataPtrBottom, (int)Stride);
						Marshal.Copy(tmpScanline, 0, new IntPtr((void*)hImageDataPtrBottom), (int)Stride);

						hImageDataPtrBottom += Stride;
						hImageDataPtrTop -= Stride;
					}
				}
			} finally {
				hImageData.Free();
			}
		}

		#endregion

		[System.Security.SuppressUnmanagedCodeSecurity()]
		[System.Runtime.InteropServices.DllImport(Library, EntryPoint = "memcpy", ExactSpelling = true)]
		internal extern static unsafe void memcpy(void *dst, void *src, int bytes);

#if WIN32
		/// <summary>
		/// Library exporting C memcpy.
		/// </summary>
		private const string Library = "msvcrt.dll";
#endif

#if __linux__
		/// <summary>
		/// Library exporting C memcpy.
		/// </summary>
		private const string Library = "libstdc.so";
#endif

		#endregion

		#region Image Creation

		/// <summary>
		/// Create an <see cref="Image"/> given image extents and pixel format.
		/// </summary>
		/// <param name="width">
		/// A <see cref="System.UInt32"/> that specifies the image width, in pixels.
		/// </param>
		/// <param name="height">
		/// A <see cref="System.UInt32"/> that specifies the image height, in pixels.
		/// </param>
		/// <param name="type">
		/// A <see cref="Pixel.Type"/> that specifies the image pixel format.
		/// </param>
		/// <returns>
		/// It returns an <see cref="Image"/> having the image extent specified by <paramref name="width"/> and
		/// <paramref name="height"/>, and the pixel format specified by <paramref name="type"/>.
		/// </returns>
		public static Image CreateImage(uint width, uint height, Pixel.Type type)
		{
			return (CreateImage(width, height, type, ModifierFlags.None));
		}

		/// <summary>
		/// Create an <see cref="Image"/> given image extents, pixel format and modifier.
		/// </summary>
		/// <param name="width">
		/// A <see cref="System.UInt32"/> that specifies the image width, in pixels.
		/// </param>
		/// <param name="height">
		/// A <see cref="System.UInt32"/> that specifies the image height, in pixels.
		/// </param>
		/// <param name="type">
		/// A <see cref="Pixel.Type"/> that specifies the image pixel format.
		/// </param>
		/// <param name="flags">
		/// A <see cref="ModifierFlags"/> that specifies special Image flags.
		/// </param>
		/// <returns>
		/// It returns an <see cref="Image"/> having the image extent specified by <paramref name="width"/> and
		/// <paramref name="height"/>, and the pixel format specified by <paramref name="type"/>.
		/// </returns>
		public static Image CreateImage(uint width, uint height, Pixel.Type type, ModifierFlags flags)
		{
			switch (type) {
				case Pixel.Type.GRAY8:
					return (new RasterImage<ColorGRAY8>(width, height, flags));
				case Pixel.Type.GRAY16:
					return (new RasterImage<ColorGRAY16>(width, height, flags));
				case Pixel.Type.GRAY32:
					return (new RasterImage<ColorGRAY32>(width, height, flags));
				case Pixel.Type.GRAYF:
					return (new RasterImage<ColorGRAYF>(width, height, flags));
				case Pixel.Type.GRAYHF:
					return (new RasterImage<ColorGRAYHF>(width, height, flags));
				case Pixel.Type.GRAYAF:
					return (new RasterImage<ColorGRAYAF>(width, height, flags));
				case Pixel.Type.RGB8:
					return (new RasterImage<ColorRGB8>(width, height, flags));
				case Pixel.Type.RGB15:
					return (new RasterImage<ColorRGB15>(width, height, flags));
				case Pixel.Type.RGB16:
					return (new RasterImage<ColorRGB16>(width, height, flags));
				case Pixel.Type.RGB24:
					return (new RasterImage<ColorRGB24>(width, height, flags));
				case Pixel.Type.RGB48:
					return (new RasterImage<ColorRGB48>(width, height, flags));
				case Pixel.Type.RGB96:
					return (new RasterImage<ColorRGB96>(width, height, flags));
				case Pixel.Type.RGBF:
					return (new RasterImage<ColorRGBF>(width, height, flags));
				case Pixel.Type.RGBD:
					return (new RasterImage<ColorRGBD>(width, height, flags));
				case Pixel.Type.RGBHF:
					return (new RasterImage<ColorRGBHF>(width, height, flags));
				case Pixel.Type.SRGB24:
					return (new RasterImage<ColorSRGB24>(width, height, flags));
				case Pixel.Type.SBGR24:
					return (new RasterImage<ColorSBGR24>(width, height, flags));
				case Pixel.Type.RGB30A2:
					return (new RasterImage<ColorRGB30A2>(width, height, flags));
				case Pixel.Type.RGBA32:
					return (new RasterImage<ColorRGBA32>(width, height, flags));
				case Pixel.Type.RGBA64:
					return (new RasterImage<ColorRGBA64>(width, height, flags));
				case Pixel.Type.RGBA128:
					return (new RasterImage<ColorRGBA128>(width, height, flags));
				case Pixel.Type.RGBAF:
					return (new RasterImage<ColorRGBAF>(width, height, flags));
				case Pixel.Type.RGBAHF:
					return (new RasterImage<ColorRGBAHF>(width, height, flags));
				case Pixel.Type.BGR8:
					return (new RasterImage<ColorBGR8>(width, height, flags));
				case Pixel.Type.BGR15:
					return (new RasterImage<ColorBGR15>(width, height, flags));
				case Pixel.Type.BGR16:
					return (new RasterImage<ColorBGR16>(width, height, flags));
				case Pixel.Type.BGR24:
					return (new RasterImage<ColorBGR24>(width, height, flags));
				case Pixel.Type.BGR48:
					return (new RasterImage<ColorBGR48>(width, height, flags));
				case Pixel.Type.BGR96:
					return (new RasterImage<ColorBGR96>(width, height, flags));
				case Pixel.Type.BGRF:
					return (new RasterImage<ColorBGRF>(width, height, flags));
				case Pixel.Type.BGRHF:
					return (new RasterImage<ColorBGRHF>(width, height, flags));
				case Pixel.Type.BGR30A2:
					return (new RasterImage<ColorBGR30A2>(width, height, flags));
				case Pixel.Type.BGRA32:
					return (new RasterImage<ColorBGRA32>(width, height, flags));
				case Pixel.Type.BGRA64:
					return (new RasterImage<ColorBGRA64>(width, height, flags));
				case Pixel.Type.BGRA128:
					return (new RasterImage<ColorBGRA128>(width, height, flags));
				case Pixel.Type.BGRAF:
					return (new RasterImage<ColorBGRAF>(width, height, flags));
				case Pixel.Type.BGRAHF:
					return (new RasterImage<ColorBGRAHF>(width, height, flags));
				case Pixel.Type.CMY24:
					return (new RasterImage<ColorCMY24>(width, height, flags));
				case Pixel.Type.CMYK32:
					return (new RasterImage<ColorCMYK32>(width, height, flags));
				case Pixel.Type.CMYK64:
					return (new RasterImage<ColorCMYK64>(width, height, flags));
				case Pixel.Type.CMYKA40:
					return (new RasterImage<ColorCMYKA40>(width, height, flags));
				default:
					throw new ArgumentException("unknown pixel type " + type, "type");
			}
		}

		/// <summary>
		/// Create an <see cref="Image"/> given a <see cref="System.Drawing.Bitmap"/>
		/// </summary>
		/// <param name="bitmap">
		/// A <see cref="System.Drawing.Bitmap"/> defining the image.
		/// </param>
		/// <returns>
		/// It returns an <see cref="Image"/> representing <paramref name="bitmap"/>.
		/// </returns>
		public static Image CreateImage(Bitmap bitmap)
		{
			ImageCodec.CodecCriteria codecCriteria = new ImageCodec.CodecCriteria();

			return (NetImagingImageCodecPlugin.LoadFromBitmap(bitmap, codecCriteria));
		}

		#endregion

		#region Image Comparing

		/// <summary>
		/// Compare pixels of two images.
		/// </summary>
		/// <param name="imageA"></param>
		/// <param name="imageB"></param>
		/// <returns></returns>
		public static bool ComparePixels(Image imageA, Image imageB)
		{
			if (imageA == null)
				throw new ArgumentNullException("imageA");
			if (imageB == null)
				throw new ArgumentNullException("imageB");
			if (imageA.Width != imageB.Width)
				throw new ArgumentException("different Width");
			if (imageA.Height != imageB.Height)
				throw new ArgumentException("different Height");
			if (imageA.PixelFormat != imageB.PixelFormat)
				throw new ArgumentException("different PixelFormat");

			Pixel.Format pFormatA = Pixel.GetPixelFormat(imageA.PixelFormat);
			Pixel.Format pFormatB = Pixel.GetPixelFormat(imageB.PixelFormat);
			byte colorCompCount = Pixel.GetComponentsCount(imageA.PixelFormat);

			for (uint y = 0 ; y < imageA.Height; y++) {
				for (uint x = 0 ; x < imageA.Width; x++) {
					for (byte c = 0; c < colorCompCount; c++) {
						double epsilonA = pFormatA.GetComponentPrecision(c);
						double epsilonB = pFormatB.GetComponentPrecision(c);
						double epsilon = Math.Max(epsilonA, epsilonB);

						if (Math.Abs(imageA[x,y][c] - imageB[x,y][c]) > epsilon)
							return (false);
					}
				}
			}

			return (true);
		}

		/// <summary>
		/// Structure defining statistic produced while comparing two images.
		/// </summary>
		public struct CompareStatistics
		{
			/// <summary>
			/// The error average based on image components differences.
			/// </summary>
			public float ComponentErrorAverage;
			/// <summary>
			/// The minimum error based on image components differences.
			/// </summary>
			public float ComponentErrorMin;
			/// <summary>
			/// The maximum error based on image components differences.
			/// </summary>
			public float ComponentErrorMax;
		}

		/// <summary>
		/// Compare two image pixel by pixel.
		/// </summary>
		/// <param name="imageA">
		/// A <see cref="Image"/> that specifies the left image to compare.
		/// </param>
		/// <param name="imageB">
		/// A <see cref="Image"/> that specifies the right image to compare.
		/// </param>
		/// <param name="stats">
		/// A <see cref="CompareStatistics"/> that groups all statistic collected during the pixel compare.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown in the case <paramref name="imageA"/> or <paramref name="imageB"/> are null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown in the case the width, the height or the pixel format of the images <paramref name="imageA"/> and
		/// <paramref name="imageB"/> are different.
		/// </exception>
		public static void ComparePixels(Image imageA, Image imageB, out CompareStatistics stats)
		{
			if (imageA == null)
				throw new ArgumentNullException("imageA");
			if (imageB == null)
				throw new ArgumentNullException("imageB");
			if (imageA.Width != imageB.Width)
				throw new ArgumentException("different Width");
			if (imageA.Height != imageB.Height)
				throw new ArgumentException("different Height");
			if (imageA.PixelFormat != imageB.PixelFormat)
				throw new ArgumentException("different PixelFormat");

			Pixel.Format pFormatA = Pixel.GetPixelFormat(imageA.PixelFormat);
			Pixel.Format pFormatB = Pixel.GetPixelFormat(imageB.PixelFormat);
			byte colorCompCount = Pixel.GetComponentsCount(imageA.PixelFormat);

			double compErrAvg = 0.0f, compErrMin = Double.MaxValue, compErrMax = -Double.MaxValue;
			uint compErrAvgCount = 0;

			for (uint y = 0 ; y < imageA.Height; y++) {
				for (uint x = 0 ; x < imageA.Width; x++) {
					for (byte c = 0; c < colorCompCount; c++) {
						double epsilonA = pFormatA.GetComponentPrecision(c);
						double epsilonB = pFormatB.GetComponentPrecision(c);
						double epsilon = Math.Max(epsilonA, epsilonB);

						double relError = (Math.Abs(imageA[x,y][c] - imageB[x,y][c]));

						compErrAvg += relError;
						compErrMin = Math.Min(compErrMin, Math.Abs(relError));
						compErrMax = Math.Max(compErrMax, Math.Abs(relError));
						compErrAvgCount++;
					}
				}
			}

			stats.ComponentErrorAverage = (float)(compErrAvg / (double)compErrAvgCount);
			stats.ComponentErrorMin = (float)compErrMin;
			stats.ComponentErrorMax = (float)compErrMax;
		}

		/// <summary>
		/// Compute an image representing the different between two images.
		/// </summary>
		/// <param name="imageA">
		/// A <see cref="Image"/> that specifies the left image to compare.
		/// </param>
		/// <param name="imageB">
		/// A <see cref="Image"/> that specifies the right image to compare.
		/// </param>
		/// <returns>
		/// 
		/// </returns>
		/// <exception cref="ArgumentNullException">
		/// Exception thrown in the case <paramref name="imageA"/> or <paramref name="imageB"/> are null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// Exception thrown in the case the width, the height or the pixel format of the images <paramref name="imageA"/> and
		/// <paramref name="imageB"/> are different.
		/// </exception>
		public static Image ComputeImageDifferences(Image imageA, Image imageB)
		{
			Image diff;
			object dataA, dataB;

			if (imageA == null)
				throw new ArgumentNullException("imageA");
			if (imageB == null)
				throw new ArgumentNullException("imageB");
			if (imageA.Width != imageB.Width)
				throw new ArgumentException("different Width");
			if (imageA.Height != imageB.Height)
				throw new ArgumentException("different Height");
			if (imageA.PixelFormat != imageB.PixelFormat)
				throw new ArgumentException("different PixelFormat");

			if ((dataA = imageA.GetPixelData()) == null)
				throw new ArgumentException("imageA not allocated");
			if ((dataB = imageB.GetPixelData()) == null)
				throw new ArgumentException("imageB not allocated");

			diff = new RasterImage<ColorRGBA32>(imageA.Width, imageA.Height);
			int colorCompCount = 3;

			for (uint y = 0 ; y < imageA.Height; y++) {
				for (uint x = 0 ; x < imageA.Width; x++) {
					for (int c = 0; c < colorCompCount; c++)
						diff[x, y][c] = Math.Abs(imageA[x,y][c] - imageA[x,y][c]);
				}
			}

			return (diff);
		}

		#endregion
	}
}
